package client

import (
	"errors"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize/v2"
	"log"
	"openeuler.org/cir/models"
	"strings"
	"sync"
)

var VectorMap map[string]map[string]string
var excelLock sync.Mutex

const (
	SecIntroduction = `An update for  %s is now available for %s`
	SecSummary      = `%s security update`
	SecTheme        = `openEuler Security has rated this update as having a security impact of medium. A Common Vunlnerability Scoring System(CVSS)base score, which gives a detailed severity rating, is available for each vulnerability from the CVElink(s) in the References section.`
	SecReference    = `https://nvd.nist.gov/vuln/detail/%s`
	SecDesc         = "%s \r\nSecurity Fix(es):\r\n%s"
	PkgUrl = "https://repo.openeuler.org/%s/update/aarch64/Packages/%s"
)

func init() {
	//pattenStr := `/AV:[NLAP]/AC:[HL]/PR:[NLH]/UI:[NR]/S:[UC]/C:[NLH]/I:[HNL]/A:[LNH]`
	VectorMap = make(map[string]map[string]string)
	mAv := make(map[string]string)
	mAv["N"] = "Network"
	mAv["L"] = "Local"
	mAv["A"] = "Adjacent"
	mAv["P"] = "Physical"
	VectorMap["AV"] = mAv
	mAc := make(map[string]string)
	mAc["H"] = "High"
	mAc["L"] = "Low"
	VectorMap["AC"] = mAc
	mPr := make(map[string]string)
	mPr["H"] = "High"
	mPr["N"] = "None"
	mPr["L"] = "Low"
	VectorMap["PR"] = mPr
	mUi := make(map[string]string)
	mUi["N"] = "None"
	mUi["R"] = "Require"
	VectorMap["UI"] = mUi
	mS := make(map[string]string)
	mS["U"] = "Unchanged"
	mS["C"] = "Changed"
	VectorMap["S"] = mS
	mC := make(map[string]string)
	mC["H"] = "High"
	mC["L"] = "Low"
	mC["N"] = "None"
	VectorMap["C"] = mC
	mI := make(map[string]string)
	mI["H"] = "High"
	mI["N"] = "None"
	mI["L"] = "Low"
	VectorMap["I"] = mC
	mA := make(map[string]string)
	mA["H"] = "High"
	mA["L"] = "Low"
	mA["N"] = "None"
	VectorMap["A"] = mA
}

type Excel struct {
	ExcelName            string         //excel 名字
	ExcelHandel          *excelize.File //excel 文件句柄
	CveSheetIdx          int            //CVE sheet index
	CveSheetName         string         // CVE sheet Name
	SecNoticeSheetIdx    int            //安全公告index
	SecNoticeSheetName   string         //安全公告sheet名字
	SecNoticePrefix      string         //安全公告前缀
	SecNoticeSuffixIdx   int64          //安全公告后缀起始值
	InfProductSheetName  string         //CVE 影响的产品
	InfProductSheetIndex int
	PackageUrlSheetName  string
	PackageUrlSheetIndex int
}

func VctToMap(vct string) (vctMap map[string]string, ok bool) {
	if vct == "" {
		return nil, false
	}
	sp := strings.Split(vct, "/")
	if len(sp) < 1 {
		return nil, false
	}
	vMap := make(map[string]string)
	for _, v := range sp {
		spv := strings.Split(v, ":")
		if len(spv) != 2 {
			continue
		}
		vMap[spv[0]] = v
	}
	if len(vMap) > 0 {
		return vMap, true
	} else {
		return nil, false
	}
}
func TrimString(str string) string {
	if str == "" {
		return str
	}
	str = strings.Replace(str, " ", "", -1)
	str = strings.Replace(str, "\n", "", -1)
	str = strings.Replace(str, "\r", "", -1)
	return str
}

func ReadVmValue(kstr string) (value string) {
	if kstr == "" {
		return ""
	}
	if !strings.Contains(kstr, ":") {
		return ""
	}
	kstr = TrimString(kstr)
	sKs := strings.Split(kstr, ":")
	if len(sKs) != 2 {
		return ""
	}
	excelLock.Lock()
	if _, ok := VectorMap[sKs[0]]; ok {
		value = VectorMap[sKs[0]][sKs[1]]
	}
	excelLock.Unlock()
	return
}

func GenerateExcel(name string) (excel Excel, err error) {
	if name == "" || !(strings.HasSuffix(name, ".xlsx") || strings.HasSuffix(name, "xls")) {
		err = errors.New("excel name illegal")
		return
	}
	excel = Excel{ExcelName: name, CveSheetName: "CVE", CveSheetIdx: 0,
		SecNoticeSheetName: "安全公告", SecNoticeSheetIdx: 1,
		SecNoticePrefix: "openEuler-SA-2020-", SecNoticeSuffixIdx: 1001,
		InfProductSheetIndex:2,InfProductSheetName: "CVE影响的产品",
	    PackageUrlSheetIndex: 3,PackageUrlSheetName: "安全公告升级包链接",
	}
	excel.ExcelHandel = excelize.NewFile()
	excel.SecNoticeSheetIdx = excel.ExcelHandel.NewSheet(excel.SecNoticeSheetName)
	excel.InfProductSheetIndex = excel.ExcelHandel.NewSheet(excel.InfProductSheetName)
	excel.PackageUrlSheetIndex = excel.ExcelHandel.NewSheet(excel.PackageUrlSheetName)
	sn := excel.ExcelHandel.GetSheetName(excel.CveSheetIdx)
	excel.ExcelHandel.SetSheetName(sn, excel.CveSheetName)
	err = excel.generateExcelHeader()
	if err != nil {
		return
	}
	if err = excel.ExcelHandel.SaveAs(excel.ExcelName); err != nil {
		return excel, err
	}
	return
}

func (e *Excel) generateExcelHeader() error {
	//generate cve sheet header
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "A1", "CVE编号")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "A1", "A2")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "B1", "概要")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "B1", "B2")

	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "C1", "Cvss Score")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "C1", "D1")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "c2", "NVD")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "D2", "OpenEuler")

	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "E1", "Attack Vector")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "E1", "F1")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "E2", "NVD")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "F2", "OpenEuler")

	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "G1", "Attack Complexity")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "G1", "H1")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "G2", "NVD")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "H2", "OpenEuler")

	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "I1", "Privileges Required")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "I1", "J1")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "I2", "NVD")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "J2", "OpenEuler")

	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "K1", "User Interaction")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "K1", "L1")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "K2", "NVD")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "L2", "OpenEuler")

	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "M1", "Scope")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "M1", "N1")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "M2", "NVD")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "N2", "OpenEuler")

	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "O1", "Confidentiality")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "O1", "P1")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "O2", "NVD")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "P2", "OpenEuler")

	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "Q1", "Integrity")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "Q1", "R1")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "Q2", "NVD")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "R2", "OpenEuler")

	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "S1", "Availability")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "S1", "T1")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "S2", "NVD")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "T2", "OpenEuler")

	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "U1", "评分类型")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "U1", "U2")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "V1", "安全公告")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "V1", "V2")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "W1", "公告时间")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "W1", "W2")
	_ = e.ExcelHandel.SetCellValue(e.CveSheetName, "X1", "gitee 仓库名")
	_ = e.ExcelHandel.MergeCell(e.CveSheetName, "X1", "X2")
	//安全公告表头生成
	sh := []interface{}{"公告编号", "CVE编号", "简介", "概要", "主题", "描述", "影响组件", "影响产品", "影响包名", "参考", "公告时间"}
	err := e.ExcelHandel.SetSheetRow(e.SecNoticeSheetName, "A1", &sh)
	if err != nil {
		return err
	}
	ih := []interface{}{"CVE编号","影响的产品","包","状态"}
	err = e.ExcelHandel.SetSheetRow(e.InfProductSheetName, "A1", &ih)
	if err != nil {
		return err
	}
	ph := []interface{}{"包名","包下载链接"}
	err = e.ExcelHandel.SetSheetRow(e.PackageUrlSheetName, "A1", &ph)
	if err != nil {
		return err
	}
	return nil

}

func (e *Excel) SetExcelRowsValue(cveValues, secValues,infValues,pkgValues [][]interface{}) error {
	excelLock.Lock()
	defer excelLock.Unlock()
	file, err := excelize.OpenFile(e.ExcelName)
	if err != nil {
		return err
	}
	e.ExcelHandel = file
	if len(cveValues) > 0 {
		err := e.setCveSheetRowsValue(cveValues)
		if err != nil {
			log.Println(err)
		}
	}
	//set sec values
	if len(secValues) > 0 {
		err := e.setSecSheetRowsValue(secValues)
		if err != nil {
			fmt.Println(err)
		}
	}
	if len(infValues) > 0 {
		err := e.setInfSheetRowsValue(infValues)
		if err != nil {
			fmt.Println(err)
		}
	}
	if len(pkgValues) >0 {
       err := e.setPkgSheetRowsValue(pkgValues)
       if err != nil{
       	fmt.Println(err)
	   }
	}
	err = e.ExcelHandel.Save()
	if err != nil {
		return err
	}
	return nil
}

func (e *Excel) setCveSheetRowsValue(values [][]interface{}) error {
	if len(values) == 0 {
		return errors.New("values is illegal")
	}
	rows, err := e.ExcelHandel.GetRows(e.CveSheetName)
	if err != nil {
		return err
	}
	sIndex := len(rows) + 1
	for _, v := range values {
		if len(v) == 0 {
			continue
		}
		axis := fmt.Sprintf("A%d", sIndex)
		err := e.ExcelHandel.SetSheetRow(e.CveSheetName, axis, &v)
		if err != nil {
			log.Println(err)
			continue
		}
		sIndex++
	}
	return nil
}

func (e *Excel) setSecSheetRowsValue(values [][]interface{}) error {
	if len(values) == 0 {
		return errors.New("values is illegal")
	}
	rows, err := e.ExcelHandel.GetRows(e.SecNoticeSheetName)
	if err != nil {
		return err
	}
	sIndex := len(rows) + 1
	for _, v := range values {
		if len(v) == 0 {
			continue
		}
		axis := fmt.Sprintf("A%d", sIndex)
		err := e.ExcelHandel.SetSheetRow(e.SecNoticeSheetName, axis, &v)
		if err != nil {
			log.Println(err)
			continue
		}
		sIndex++
	}
	return nil
}

func (e *Excel) setInfSheetRowsValue(values [][]interface{}) error {
	if len(values) == 0 {
		return errors.New("values is illegal")
	}
	rows, err := e.ExcelHandel.GetRows(e.InfProductSheetName)
	if err != nil {
		return err
	}
	sIndex := len(rows) + 1
	for _, v := range values {
		if len(v) == 0 {
			continue
		}
		axis := fmt.Sprintf("A%d", sIndex)
		err := e.ExcelHandel.SetSheetRow(e.InfProductSheetName, axis, &v)
		if err != nil {
			log.Println(err)
			continue
		}
		sIndex++
	}
	return nil
}

func (e *Excel) setPkgSheetRowsValue(values [][]interface{}) error {
	if len(values) == 0 {
		return errors.New("values is illegal")
	}
	rows, err := e.ExcelHandel.GetRows(e.PackageUrlSheetName)
	if err != nil {
		return err
	}
	sIndex := len(rows) + 1
	for _, v := range values {
		if len(v) == 0 {
			continue
		}
		axis := fmt.Sprintf("A%d", sIndex)
		err := e.ExcelHandel.SetSheetRow(e.PackageUrlSheetName, axis, &v)
		if err != nil {
			log.Println(err)
			continue
		}
		sIndex++
	}
	return nil
}

func (e *Excel) ParseLoopholeToExcelValues(data []models.Loophole) (cveValues, secValues,infValue,pkgValue [][]interface{}) {
	if len(data) == 0 {
		return
	}
	cve := make([][]interface{}, len(data))
	sec := make([][]interface{}, len(data))
	inf := make([][]interface{},len(data))
	pkg := make([][]interface{},0)
	for i, v := range data {
		excelLock.Lock()
		sIdx := e.SecNoticeSuffixIdx
		sIdx = sIdx + 1
		e.SecNoticeSuffixIdx = sIdx
		excelLock.Unlock()
		vctValue := e.VectorToExcelValue(v.CvsVector, v.OpVector)
		secName := fmt.Sprintf("%s%d", e.SecNoticePrefix, e.SecNoticeSuffixIdx)
		cveNumber := ""
		if strings.Contains(v.Number, "(http") {
			start := strings.Index(v.Number, "[") + 1
			end := strings.Index(v.Number, "]")
			cveNumber = v.Number[start:end]
		} else {
			cveNumber = v.Number
		}
		itemCve := []interface{}{cveNumber, v.BriefIntroduction, v.CvsScore, v.OpScore}
		itemCve = append(itemCve, vctValue...)
		itemCve = append(itemCve, "V3", secName, "2020-08-13", v.Repo)
		cve[i] = itemCve
		si := fmt.Sprintf(SecIntroduction, v.Components, v.InfProduct)
		sm := fmt.Sprintf(SecSummary, v.Components)
		st := fmt.Sprintf("%s\r\n%s", si, SecTheme)
		sp := TrimString(v.InfPackage)
		if sp != "" {
			sp = strings.Replace(sp,";","",-1)
			sp = strings.Replace(sp,".rpm",".rpm;",-1)
			spList := strings.Split(sp, ";")
			lIdx := len(spList)-1
			if !strings.HasSuffix(spList[lIdx],".rpm"){
              spList = spList[:lIdx]
			}
			if len(spList)>0{
				for _,val := range spList{
					pkgItem := []interface{}{val,fmt.Sprintf(PkgUrl,v.InfProduct,val)}
					pkg = append(pkg, pkgItem)
				}
			}

			sp = strings.Join(spList,"")
		}
		sr := fmt.Sprintf(SecReference, cveNumber)
		sd := fmt.Sprintf(SecDesc, v.RepoDesc, v.BriefIntroduction)
		secItem := []interface{}{secName, cveNumber, si, sm, st, sd, v.Components, v.InfProduct, sp, sr, "2020-08-31"}
		sec[i] = secItem
		infItem := []interface{}{cveNumber,v.InfProduct,v.Components,"Fixed"}
		inf[i] =infItem
	}
	return cve, sec,inf,pkg
}

func (e *Excel) VectorToExcelValue(nvdV, opV string) (res []interface{}) {
	nvdV = TrimString(nvdV)
	opV = TrimString(opV)
	res = make([]interface{}, 16)
	if nvdV == "" && opV == "" {
		return
	}
	// /AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
	nvdMap, ok := VctToMap(nvdV)
	if ok {
		//处理NVD VECTOR
		res[0] = ReadVmValue(nvdMap["AV"])
		res[2] = ReadVmValue(nvdMap["AC"])
		res[4] = ReadVmValue(nvdMap["PR"])
		res[6] = ReadVmValue(nvdMap["UI"])
		res[8] = ReadVmValue(nvdMap["S"])
		res[10] = ReadVmValue(nvdMap["C"])
		res[12] = ReadVmValue(nvdMap["I"])
		res[14] = ReadVmValue(nvdMap["A"])
	}
	opMap, ok := VctToMap(opV)
	if ok {
		//处理 openEuler vector
		res[1] = ReadVmValue(opMap["AV"])
		res[3] = ReadVmValue(opMap["AC"])
		res[5] = ReadVmValue(opMap["PR"])
		res[7] = ReadVmValue(opMap["UI"])
		res[9] = ReadVmValue(opMap["S"])
		res[11] = ReadVmValue(opMap["C"])
		res[13] = ReadVmValue(opMap["I"])
		res[15] = ReadVmValue(opMap["A"])
	}
	return
}
